/**
 * The ColliePropertyPanel is a placeholder panel which will be used to
 * display the properties of selected model elements.
 * 
 * This class implements the singleton design pattern.
 * 
 * @author K Barclay
 */

package window;

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

import diagram.CollaborationDiagram;
import diagram.ElementSelectionEvent;
import diagram.ElementSelectionListener;
import diagram.NodeElement;

// class ColliePropertyPanel
public final class ColliePropertyPanel extends JPanel {

	/**
	 * UID pour la version de la classe en serialisation Java.
	 */
	private static final long serialVersionUID = -4878490115527550793L;

	private SelectionListener selectionListener = new SelectionListener();
	
	/**
	 * The NodeElement we are editing.
	 */
	private NodeElement model;

	/**
	 * The panel containing the name and classifier of the NodeElement.
	 */
	private JPanel fixDataPanel;

	/**
	 * The label of the text field to edit the name of the NodeElement.
	 */
	private JLabel nameLabel;

	private JTextField nameField;

	// Class of the instance
	private JLabel classLabel;

	private JTextField classField;

	private  DefaultTableModel attributesTableModel;

	private  TableModelListener attributesChangeListener = new UpdateModelTableListener();

	private  JTable attributesTable;
	
	private MouseListener doubleClickListener = new DoubleClickListener();


	public ColliePropertyPanel(CollaborationDiagram diagram) {
		// Calling the super class constructeur
				super(new BorderLayout());
				fixDataPanel = new JPanel(new GridBagLayout());

				// Building the selection grid
				buildNameLabel();
				buildNameField();
				buildClassifierLabel();
				buildClassifierField();
				add(fixDataPanel, BorderLayout.NORTH);

				// Configuration of the attributes table
				// Table Header
				final Vector<String> headers = new Vector<String>();
				headers.add("Attribute name");
				headers.add("Attribute value");
				// Instantiate the table model
				attributesTableModel = new DefaultTableModel(headers, 1);
				// Listen to modification to transfer data to the NodeElement
				attributesTableModel.addTableModelListener(attributesChangeListener);
				// Instantiate the Table component
				attributesTable = new JTable(attributesTableModel);
				// Let the table fill the vertical space
				attributesTable.setFillsViewportHeight(true);
				// Listen to double-click to add a new row to the table
				attributesTable.addMouseListener(doubleClickListener);
				// Use a scrolling pane to render properly the table and its header
				final JScrollPane scrollPane = new JScrollPane(attributesTable);
				// Add the scrolling pane to this property panel
				add(scrollPane, BorderLayout.CENTER);
		diagram.addElementSelectionListener(selectionListener);
	}

	/**
	 * Setter of the NodeElement property.
	 * 
	 * @param pNodeElement
	 *            the new NodeElement reference.
	 */
	public void setModel(final NodeElement pNodeElement) {
		model = pNodeElement;
		if (null == pNodeElement) {
			nameField.setText("");
			nameField.setEnabled(false);
			classField.setText("");
			classField.setEnabled(false);
			
			attributesTableModel
			.removeTableModelListener(attributesChangeListener);
	// Clear the table model
	for (int i = attributesTableModel.getRowCount() - 1; i != -1; i--) {
		attributesTableModel.removeRow(i);
		
		attributesTable.setEnabled(false);
	}
		} else {
			nameField.setText(pNodeElement.getName());
			nameField.setEnabled(true);
			classField.setText(pNodeElement.getClassifier());
			classField.setEnabled(true);
			// remove temporarily the listener to prevent from clearing the
			// attributes list
			attributesTableModel
					.removeTableModelListener(attributesChangeListener);
			// Clear the table model
			for (int i = attributesTableModel.getRowCount() - 1; i != -1; i--) {
				attributesTableModel.removeRow(i);
			}

			for (int i = 0; i < pNodeElement.getAttributes().size(); i++) {
				attributesTableModel.insertRow(i, (String[]) pNodeElement
						.getAttributes().get(i));
			}

			// Put a new line
			attributesTableModel.insertRow(attributesTableModel.getRowCount(),
					new String[2]);

			// Set batck the listener
			attributesTableModel
					.addTableModelListener(attributesChangeListener);
			attributesTable.setEnabled(true);
		}
	}

	// ---------- properties ----------------------------------

	

	private void buildClassifierField() {
		// Instantiate the component
		classField = new JTextField(8);
		// Disable it by default to avoid editing without a selection
		classField.setEnabled(false);
		// Place a listener to transfer text updates to the model element
		classField.getDocument().addDocumentListener(
				new ClassifierDocumentListener());
		// Link the label to the field
		classLabel.setLabelFor(classField);

		// Constraints for the layout
		final GridBagConstraints constraints = new GridBagConstraints();
		// First cell of the second row
		constraints.gridx = 1;
		constraints.gridy = 1;
		// Grab a full size of the extra space
		constraints.weightx = 1;
		// Grab a full size of the extra space
		constraints.weightx = 1;
		// Expand the component to fill all its display area
		constraints.fill = GridBagConstraints.HORIZONTAL;

		fixDataPanel.add(classField, constraints);
	}

	private void buildClassifierLabel() {
		// Instantiate the component
		classLabel = new JLabel("Instance Classifier");
		// Specify how the text renders inside the component
		nameLabel.setHorizontalTextPosition(SwingConstants.LEADING);

		// Constraints for the layout
		final GridBagConstraints constraints = new GridBagConstraints();
		// First cell of the second row
		constraints.gridx = 0;
		constraints.gridy = 1;
		// Grab 10% of the extra space
		constraints.weightx = 0.1;
		// Expand the component to fill all its display area
		constraints.fill = GridBagConstraints.HORIZONTAL;
		// Place a 5px space gap on the on left and right
		constraints.insets.left = 5;
		constraints.insets.right = 5;

		// Now add it to this JPanel instance
		fixDataPanel.add(classLabel, constraints);
	}

	private void buildNameField() {
		// Instantiate the component
		nameField = new JTextField(8);
		// Disable it by default to avoid editing without a selection
		nameField.setEnabled(false);
		// Place a listener to transfer text updates to the model element
		nameField.getDocument().addDocumentListener(new NameDocumentListener());
		// Link the label to the field
		nameLabel.setLabelFor(nameField);

		// Constraints for the layout
		final GridBagConstraints constraints = new GridBagConstraints();
		// Second cell of the first row
		constraints.gridx = 1;
		constraints.gridy = 0;
		// Grab a full size of the extra space
		constraints.weightx = 1;
		// Expand the component to fill all its display area
		constraints.fill = GridBagConstraints.HORIZONTAL;

		// Now add it to this JPanel instance
		fixDataPanel.add(nameField, constraints);
	}

	private void buildNameLabel() {
		// Instantiate the component
		nameLabel = new JLabel("Instance Name");
		// Specify how the text renders inside the component
		nameLabel.setHorizontalTextPosition(SwingConstants.LEADING);

		// Constraints for the layout
		final GridBagConstraints constraints = new GridBagConstraints();
		// First cell of the first row
		constraints.gridx = 0;
		constraints.gridy = 0;
		// Grab 10% of the extra space
		constraints.weightx = 0.1;
		// Expand the component to fill all its display area
		constraints.fill = GridBagConstraints.HORIZONTAL;
		// Place a 5px space gap on the on left and right
		constraints.insets.left = 5;
		constraints.insets.right = 5;

		// Now add it to this JPanel instance
		fixDataPanel.add(nameLabel, constraints);
	}
	
	private abstract class BaseDocumentListener implements DocumentListener {

		@Override
		public void insertUpdate(final DocumentEvent e) {
			updateText(e);
		}

		@Override
		public void removeUpdate(final DocumentEvent e) {
			updateText(e);
		}

		@Override
		public void changedUpdate(final DocumentEvent e) {
			updateText(e);
		}

		protected abstract void setNewText(String newText);

		private void updateText(final DocumentEvent e) {
			final Document document = e.getDocument();
			try {
				final String newText = document
						.getText(0, document.getLength());
				setNewText(newText);
			} catch (final BadLocationException e1) {
				e1.printStackTrace();
			}
		}
	}

	private class NameDocumentListener extends BaseDocumentListener {

		@Override
		protected void setNewText(final String newText) {
			if (null != model) {
				model.setName(newText);
			}
		}
	}

	private class ClassifierDocumentListener extends BaseDocumentListener {

		@Override
		protected void setNewText(final String newText) {
			if (null != model) {
				model.setClassifier(newText);
			}
		}
	}

	private class SelectionListener implements ElementSelectionListener {
		@Override
		public void elementSelected(ElementSelectionEvent event) {
			setModel((NodeElement) event.getSource());
		}

		@Override
		public void elementDeselected(ElementSelectionEvent event) {
			setModel(null);
		}
	}
	
	private class DoubleClickListener extends MouseAdapter {

		@Override
		public void mouseClicked(final MouseEvent pE) {
			if (1 < pE.getClickCount()) {
				// double click
				final int numberOfRows = attributesTableModel.getRowCount();
				final Object lastAttribute = attributesTableModel
						.getValueAt(numberOfRows - 1, 0);
				// if the attribute name of the last row is set
				if ((null != lastAttribute)
						&& !"".equals(lastAttribute.toString().trim())) {
					// then add a new row to the model
					attributesTableModel.insertRow(numberOfRows,
							new String[2]);
				}
			}
		}
	}
	
	private class UpdateModelTableListener implements TableModelListener {

		@Override
		public void tableChanged(final TableModelEvent pE) {
			if (null != model) {
			final List<String[]> newAttributesList = new ArrayList<String[]>(
					attributesTableModel.getRowCount());
			// Extract attributes from the table model
			for (int i = 0; i < attributesTableModel.getRowCount(); i++) {
				final Object firstColumnValue = attributesTableModel
						.getValueAt(i, 0);
				final Object secondColumnValue = attributesTableModel
						.getValueAt(i, 1);
				if ((null != firstColumnValue)
						&& !"".equals(firstColumnValue.toString().trim())) {
					if (secondColumnValue != null) {
						newAttributesList.add(new String[] {
								firstColumnValue.toString().trim(),
								secondColumnValue.toString().trim() });
					}	else {
						newAttributesList.add(new String[] {
								firstColumnValue.toString().trim(),null});
					}
				}
			}
			// Put extracted attributes in the NodeElement instance
			model.getAttributes().clear();
			model.getAttributes().addAll(newAttributesList);
			}
		}

	}
	
}
// ==================================================================
